#ifndef SRS_RTU_STACK_HPP
#define SRS_RTU_STACK_HPP

#include <map>
#include <vector>
#include <string>
#include <sys/types.h>

#include "srs_core.hpp"
#include "srs_kernel_error.hpp"
#include "srs_kernel_consts.hpp"
#include "srs_kernel_buffer.hpp"
#include "srs_kernel_container.hpp"
#include "srs_kernel_code_delay.hpp"
#include "srs_kernel_stream.hpp"


// for rtu global
#define SRS_RTU_PACKET_SIZE_LIMIT   1300
#define SRS_RTU_PACKET_DATA_SIZE_LIMIT   (1300 - 39)

// for rtu listener
#define SRS_RTU_SERVER_DEFAULT_PORT "8899"

// for rtu source
#define SRS_RTU_CACHE_DURATION_MS   5000
#define SRS_RTU_FIRST_MESSAGE_NO    10
#define SRS_RTU_FIRST_PACKET_NO     10

// for rtu connection
#define SRS_RTU_DATA_SEND_INTERVAL_US 5
#define SRS_RTU_RTNS_SEND_INTERVAL_US 20

// for rtu rtns
#define SRS_RTU_SORT_WAIT_PACKET_NUM    500  //50
#define SRS_RTU_BUFFER_PACKET_NUM       2500  //1500

// for rtu thread
#define SRS_RTU_THREAD_WAIT_TIME    (1*1000)
#define SRS_RTU_RTNS_WAIT_TIME      (10*1000)

// for heartbeat
#define SRS_RTU_HEARTBEAT_INTERVAL_MS	5000
#define SRS_RTU_HEARTBEAT_EXPIRE_MS		20000


class SrsPacket;

enum SrsRtuCommand
{
    SrsRtuCmdUnknown                    = 0,
    SrsRtuCmdStreamStart                = 1,
    SrsRtuCmdStreamStartResponse        = 2,
    SrsRtuCmdStreamData                 = 3,
    SrsRtuCmdRetransmissionRequest      = 4,
    SrsRtuCmdRetransmissionResponse     = 5,
    SrsRtuCmdStreamStop                 = 6,
    SrsRtuCmdHeartbeat					= 7,
	SrsRtuCmdStreamPush                 = 8,
	SrsRtuCmdStreamPushStop             = 9
};

enum SrsRtuStreamDataType
{
    SrsRtuStreamDataTypeUnknown = 0,
    SrsRtuStreamDataTypeMeta    = 1,
    SrsRtuStreamDataTypeAudio   = 2,
    SrsRtuStreamDataTypeVideo   = 3,
    SrsRtuStreamDataTypeAudioSh	= 4,
    SrsRtuStreamDataTypeVideoSh = 5,
    SrsRtuStreamDataTypeVideoKeyframe = 6,
};

enum SrsRtuErrorCode
{
    SrsRtuErrorSuccess          = 0,
    SrsRtuErrorTolerantFault    = 1,
    SrsRtuErrorSourceNotFound   = -1,
    SrsRtuErrorDuplicateRequest = -2,
    SrsRtuErrorInvalidRequest   = -3,
};

class SrsRtuPacket: public RtuDataUnit
{
public:
    /**
     * @brief command
     *  the SrsRtuCommand number.
     *  indicate the command for rtu.
     */
    WORD command;
protected:
    /**
     * @brief length
     *  the whole length of packet include command & length (6)
     */
    DWORD length;
public:
    SrsRtuPacket() {}
    virtual ~SrsRtuPacket() {}
public:
    /**
     * the subpacket can override this encode,
     * for example, video and audio will directly set the payload withou memory copy,
     * other packet which need to serialize/encode to bytes by override the
     * get_size and encode_packet.
     */
    virtual int encode(int& size, char*& payload);
    // encode functions for concrete packet to override.
    /**
     * subpacket can override to encode the payload to stream.
     * @remark never invoke the super.encode_packet, it always failed.
     */
    virtual int encode_packet(SrsStream* stream);
public:
    // decode functions for concrete packet to override.
    /**
     * subpacket must override to decode packet from stream.
     * @remark never invoke the super.decode, it always failed.
     */
    virtual int decode(SrsStream* stream);
public:
    /**
     * subpacket can override to calc the packet size.
     */
    virtual int get_size();
};

/**
 * @brief The SrsRtuStreamStartPacket class
 *  client will send this packet to server to trigger stream start.
 */
class SrsRtuStreamStartPacket: public SrsRtuPacket
{
public:

    char	    url[200];   ///< the source url, should not include the schema, example: vhost/app/stream.
    DWORD	ip;         ///< the server will send stream data to this ip.
    DWORD	data_port;  ///< the server will send stream data to this port.
    DWORD	retransmission_port;    ///< the server will send retransmission data to this port.
public:
    SrsRtuStreamStartPacket();
    virtual ~SrsRtuStreamStartPacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
 * @brief The SrsRtuStreamStartPacket class
 *  sever will send this packet to client.
 *  sever will
 */
class SrsRtuStreamStartResponsePacket: public SrsRtuPacket
{
public:
    int16_t     error_code; ///< the status for stream start request.
    char	    url[200];	///< the source url, should not include the schema, example: vhost/app/stream.
    DWORD	stream_id;	///< every source will assign a unique integer stream_id
    DWORD	byterate;		///< the stream bitrate in byte, only meaningful when return success
public:
    SrsRtuStreamStartResponsePacket();
    virtual  ~SrsRtuStreamStartResponsePacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};



/**
 * @brief The SrsRtuStreamDataPacket class
 *  the server will send this packe to client include stream data.
 *  the packet include audio, video or the metadata.
 *  the data SrsRtuStreamDataPacket  may also show in the SrsRtuRtnsResponse packet,
 *  so the command could be SrsRtuCmdStreamData or SrsRtuCmdRetransmissionResponse.
 */
class SrsRtuStreamDataPacket: public SrsRtuPacket
{
public:
    DWORD	stream_id;  ///< every source will assign a unique integer stream_id
    UCHAR   stream_type;    ///< the data type video, audio or metadata
    DWORD	timestamp;      ///< the time infomation refernce to include data
    DWORD	packet_no;      ///< the unique integer for packet between stream start & stop, should always increment by one
    DWORD	video_packet_no;      ///< the unique integer for video packet between stream start & stop, should always increment by one
    DWORD	audio_packet_no;      ///< the unique integer for audio packet between stream start & stop, should always increment by one
    DWORD   message_no;     ///< the unique integer for message ( before fragment into packets).
    DWORD	slice_sum;      ///< one frame may cut to slices, slice_sum indicate the sum of slices in one frame
    DWORD	slice_no;       ///< the slice number in slices of one frame
public:
    int         payload_size;
    char        buff[SRS_RTU_PACKET_SIZE_LIMIT];
public:
    RtuCodeDelay* _delay;
public:
    SrsRtuStreamDataPacket(WORD cmd = SrsRtuCmdStreamData);
    virtual ~SrsRtuStreamDataPacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
public:
    virtual int set_payload(char* p, int size);
    virtual char* payload();
    virtual int encode_inside();
public:
    virtual SrsRtuStreamDataPacket* copy();
public:
    SrsRtuStreamDataPacket& operator=(SrsRtuStreamDataPacket& right);
};

/**
 * @brief The SrsRtuRtnsRequestPacket class
 *  the client will send this packet to get missing packet.
 */
class SrsRtuRtnsRequestPacket: public SrsRtuPacket
{
public:
    DWORD    stream_id;
	UCHAR     stream_type;

    /**
     * the important priority of the retransmission level, should be 1, 2 or 3.
     * and the sever will try 1, 2 or 3 times to send missing packet according to this packet.
     */
    UCHAR                 level;
private:
    DWORD                packet_sum;
public:
    std::vector<DWORD>   packet_no_list;
public:
    SrsRtuRtnsRequestPacket();
    virtual ~SrsRtuRtnsRequestPacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
 * @brief The SrsRtuRtnsResponseEncodePacket class
 *  the response packet to SrsRtuRtnsRequestPacket.
 *  server will send missing data to client.
 */
class SrsRtuRtnsResponseEncodePacket: public SrsRtuPacket
{
public:
    /**
     * @brief rtns_queue
     * the missing stream data packets will join into one packet.
     * but the packet command should be SrsRtuCmdRetransmissionResponse.
     */
    std::vector<SrsRtuStreamDataPacket*> pkts;
    char buff[SRS_RTU_PACKET_SIZE_LIMIT];
private:
    UCHAR level;
public:
    SrsRtuRtnsResponseEncodePacket();
    virtual ~SrsRtuRtnsResponseEncodePacket();
public:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
    virtual int encode_inside();
public:
    virtual void enqueue(SrsRtuStreamDataPacket* sh_packet);
    virtual void clear();
public:
    virtual void set_level(UCHAR rtns_level);
    virtual UCHAR get_level();
};

/**
 * @brief The SrsRtuRtnsResponseDecodePacket class
 *  Decode the  retransmission response packet
 */
class SrsRtuRtnsResponseDecodePacket: public SrsRtuPacket
{
public:
    std::vector<SrsRtuStreamDataPacket*> pkts;
public:
    SrsRtuRtnsResponseDecodePacket();
    virtual ~SrsRtuRtnsResponseDecodePacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
public:
    virtual void clear();
};

/**
 * @brief The SrsRtuStreamStopPacket class
 *  client send this packe to server,
 *  then the server will stop send the stream data packet.
 */
class SrsRtuStreamStopPacket: public SrsRtuPacket
{
public:
    DWORD    stream_id;
public:
    SrsRtuStreamStopPacket();
    virtual ~SrsRtuStreamStopPacket();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
 * @brief The SrsRtuHeartbeat class
 */
class SrsRtuHeartbeat: public SrsRtuPacket
{
public:
    DWORD    stream_id;
    DWORD    data;
public:
    // packet length 6 + 4 + 4
    char buff[14];
public:
    SrsRtuHeartbeat();
    SrsRtuHeartbeat(DWORD id);
    virtual ~SrsRtuHeartbeat();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
public:
    virtual int encode_inside();
};


/**
 *
 */
class SrsRtuStreamPush: public SrsRtuPacket{
public:
    char	    url[200];	///< the source url, should not include the schema, example: vhost/app/stream.
public:
    SrsRtuStreamPush();
    virtual  ~SrsRtuStreamPush();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

class SrsRtuStreamStopPush: public SrsRtuPacket
{
public:

    char	    url[200];   ///< the source url, should not include the schema, example: vhost/app/stream.
    DWORD	ip;         ///< the server will send stream data to this ip.
    DWORD	data_port;  ///< the server will send stream data to this port.
    DWORD	retransmission_port;    ///< the server will send retransmission data to this port.
public:
    SrsRtuStreamStopPush();
    virtual ~SrsRtuStreamStopPush();
public:
    virtual int decode(SrsStream* stream);
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};



#pragma pack(push)
#pragma pack(1)

/**
 *  To tranmission RTMP message in the RTU protocol,
 *	copy SrsMessageHeader and the SrsCommonMessage::size to
 *  this struct.
 *  then the struct will copy into the SrsRtuStreamDataPacket payload.
 */
typedef struct SrsRtuRtmpHeader
{
public:
    INT32 timestamp_delta;
    INT32 payload_length;
    CHAR message_type;
    INT32 stream_id;
    int64_t timestamp;
    INT32 perfer_cid;
    INT32 size;
} SrsRtuRtmpHeader;

#pragma pack(pop)

#pragma pack(push)
#pragma pack(1)
typedef struct SrsRtuDataHeader
{
    WORD 	command;
    DWORD 	length;
    DWORD	stream_id;  ///< every source will assign a unique integer stream_id
    UCHAR	    stream_type;    ///< the data type video, audio or metadata
    DWORD	timestamp;      ///< the time infomation refernce to include data
    DWORD	packet_no;      ///< the unique integer for packet between stream start & stop, should always increment by one
    DWORD    message_no;     ///< the unique integer for message ( before fragment into packets).
    DWORD	slice_sum;      ///< one frame may cut to slices, slice_sum indicate the sum of slices in one frame
    DWORD	slice_no;       ///< the slice number in slices of one frame
} SrsRtuDataHeader;
#pragma pack(pop)



extern void rtu_usleep(int usec);

extern int rtu_sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, int addrlen);

extern int64_t rtu_get_system_time_us();

#endif // SRS_RTU_STACK_HPP
